;
; DBGROM.MAC - A ROM to debug Colecovision hardware with
;

.Z80
ASEG

	ORG	100H

	INCLUDE	CVKBD.INC

;
; VDP register values
;

PATGEN	EQU	0
PATCOL	EQU	8*1024
PATNAM	EQU	6*1024
SPRATT	EQU	16*1024-128
SPRGEN	EQU	14*1024

;
; RAM usage
;
SCRBUF	EQU	06300H
STKTOP	EQU	06400H

.PHASE 08000H

;...sROM header:0:
BASE:	DB	0AAH,055H
	DW	0
	DW	0
	DW	0
	DW	0
	DW	START
; 0800CH
	JP	RST08V
	JP	RST10V
	JP	RST18V
	JP	RST20V
	JP	RST28V
	JP	RST30V
	JP	RST38V
	JP	NMIV
; 08024H
	DB	'COLECOVISION KEYBOARD '
	DB	'/'
	DB	'DEBUG ROM/2013'
;...e

LF	EQU	10
CR	EQU	13
ESC	EQU	27

MSGINI:	DB	'Keyboard Debug ROM, @ 2015 #$',CR
	DB	CR
	DB	'Sample commands',CR
	DB	'  i12    IN from port 12',CR
	DB	'  o1234  OUT 34 to port 12',CR
	DB	'  rabcd  READ memory at abcd',CR
	DB	'  wabcd  WRITE memory at abcd',CR
	DB	'  kabcd  CALL abcd',CR
	DB	CR,0
MSGKNF:	DB	'Keyboard not found',CR,0
MSGPRO:	DB	'> ',0
MSGABT:	DB	' aborted',CR,0
;
; Main entry routine
;
START:

;...sinitalisation:0:
	LD	SP,STKTOP

	CALL	SNDOFF

; video registers
	LD	DE,0*100H+010B		; Grf Mode II
	CALL	VOUTRG
	LD	DE,1*100H+0C2H		; bit 6 => on, bit 5 => ints disabled
	CALL	VOUTRG
	LD	DE,2*100H+PATNAM/0400H
	CALL	VOUTRG
	LD	DE,3*100H+PATCOL/0040H+127
	CALL	VOUTRG
	LD	DE,4*100H+PATGEN/0800H+3
	CALL	VOUTRG
	LD	DE,5*100H+SPRATT/0080H
	CALL	VOUTRG
	LD	DE,6*100H+SPRGEN/0800H
	CALL	VOUTRG
	LD	DE,7*100H+1
	CALL	VOUTRG

; setup characters in each 1/3 of the screen
	LD	HL,CHARSET
	LD	DE,PATGEN+32*8
	LD	BC,96*8
	CALL	VCOPY
	LD	HL,CHARSET
	LD	DE,PATGEN+256*8+32*8
	LD	BC,96*8
	CALL	VCOPY
	LD	HL,CHARSET
	LD	DE,PATGEN+512*8+32*8
	LD	BC,256*8
	CALL	VCOPY

; every character is white on black
	LD	A,0F1H
	LD	DE,PATCOL
	LD	BC,3*256*8
	CALL	VSET

; initially clear the screen
	LD	A,' '
	LD	DE,PATNAM
	LD	BC,32*24
	CALL	VSET

; no sprites visible
	LD	DE,SPRATT
	CALL	VSETOT
	LD	C,0D0H
	CALL	VDOUTP
;
; Column on the screen is in A'
;
	LD	A,1
	EX	AF,AF'

; sign-on
	LD	HL,MSGINI
	CALL	PRTSTR

;
; Keyboard present?
;
	CALL	KB_HW
	JR	Z,GOTKBD
;
	LD	HL,MSGKNF
	CALL	PRTSTR
STOP:	JR	STOP
;
; Initialise the keyboard
;
GOTKBD:	CALL	KB_INIT

;
;...e
;...smain loop:0:
;
; Main loop
;
LOOP:	LD	HL,MSGPRO
	CALL	PRTSTR
LOOP2:	CALL	GETKEY
	CP	'i'
	JP	Z,DOIN
	CP	'o'
	JP	Z,DOOUT
	CP	'r'
	JP	Z,DOREAD
	CP	'w'
	JP	Z,DOWRIT
	CP	'k'
	JP	Z,DOCALL
	JR	LOOP2
;
ABORT:	LD	HL,MSGABT
	CALL	PRTSTR
	JR	LOOP
;...e

;...sDOIN:0:
;
; Input from port
;
DOIN:	LD	HL,MSGIN
	CALL	PRTSTR
	CALL	GETBYT
	JP	C,ABORT
	PUSH	AF
	LD	HL,MSGIN2
	CALL	PRTSTR
	POP	BC
	LD	C,B
	IN	A,(C)
	CALL	PRTBYT
	LD	HL,MSGIN3
	CALL	PRTSTR
	JP	LOOP
;
MSGIN:	DB	'IN (',0
MSGIN2:	DB	') -> ',0
MSGIN3:	DB	CR,0
;...e
;...sDOOUT:0:
;
; Output to port
;
DOOUT:	LD	HL,MSGOUT
	CALL	PRTSTR
	CALL	GETBYT
	JP	C,ABORT
	PUSH	AF
	LD	HL,MSGOU2
	CALL	PRTSTR
	CALL	GETBYT
	POP	BC
	JP	C,ABORT
	LD	C,B
	OUT	(C),A
	LD	HL,MSGOU3
	CALL	PRTSTR
	JP	LOOP
;
MSGOUT:	DB	'OUT (',0
MSGOU2:	DB	'),',0
MSGOU3:	DB	CR,0
;...e
;...sDOREAD:0:
;
; Read memory
;
DOREAD:	LD	HL,MSGRD
	CALL	PRTSTR
	CALL	GETWOR
	JP	C,ABORT
	PUSH	HL
	LD	HL,MSGRD2
	CALL	PRTSTR
	POP	HL
;
DOREA2:	PUSH	HL
	CALL	PRTWOR
	POP	HL
	LD	B,8
;
DOREA4:	PUSH	BC
	PUSH	HL
	LD	A,' '
	CALL	PRTCHR
	POP	HL
	PUSH	HL
	LD	A,(HL)
	CALL	PRTBYT
	POP	HL
	INC	HL
	POP	BC
	DJNZ	DOREA4
;
	CALL	GETKEY
	PUSH	AF
	PUSH	HL
	LD	A,CR
	CALL	PRTCHR
	POP	HL
	POP	AF
;
	CP	ESC
	JR	NZ,DOREA2
;
	JP	LOOP
;
MSGRD:	DB	'READ ',0
MSGRD2:	DB	' (Esc to exit)',CR,0
MSGRD3:	DB	CR,0
;...e
;...sDOWRIT:0:
;
; Do write
;
DOWRIT:	LD	HL,MSGWRI
	CALL	PRTSTR
	CALL	GETWOR
	JP	C,ABORT
	PUSH	HL
	LD	HL,MSGWR2
	CALL	PRTSTR
	POP	HL
;
DOWRI2:	PUSH	HL
	CALL	PRTWOR
	POP	HL
	LD	B,8
;
DOWRI4:	PUSH	BC
	PUSH	HL
	LD	A,' '
	CALL	PRTCHR
	CALL	GETBYT
	POP	HL
	JR	C,DOWRI6
	LD	(HL),A
	INC	HL
	POP	BC
	DJNZ	DOWRI4
;
	PUSH	HL
	LD	A,CR
	CALL	PRTCHR
	POP	HL
	JR	DOWRI2
;
DOWRI6:	LD	HL,MSGWR3
	CALL	PRTSTR
	JP	LOOP
;
MSGWRI:	DB	'WRITE ',0
MSGWR2:	DB	' (Esc to exit)',CR,0
MSGWR3:	DB	CR,0

;...e
;...sDOCALL:0:
;
; Do call
;
DOCALL:	LD	HL,MSGCAL
	CALL	PRTSTR
	CALL	GETWOR
	JP	C,ABORT
	CALL	DOCAL2
	PUSH	HL
	PUSH	AF
	LD	HL,MSGCA2
	CALL	PRTSTR
	POP	AF
	CALL	PRTBYT
	LD	HL,MSGCA3
	CALL	PRTSTR
	POP	HL
	CALL	PRTWOR
	LD	HL,MSGCA4
	CALL	PRTSTR
	JP	LOOP
;
DOCAL2:	JP	(HL)		; really JP HL
;
MSGCAL:	DB	'CALL ',0
MSGCA2:	DB	' -> A=',0
MSGCA3:	DB	',HL=',0
MSGCA4:	DB	CR,0
;...e

;...sGETWOR:0:
;
; Get word into HL
;   CY=0, HL = word
;   CY=1 otherwise
;
GETWOR:	CALL	GETBYT
	RET	C
	LD	H,A
	PUSH	HL
	CALL	GETBYT
	POP	HL
	RET	C
	LD	L,A
	RET			; NC
;...e
;...sGETBYT:0:
;
; Get byte into A
;   CY=0, A = byte
;   CY=1 otherwise
;
GETBYT:	CALL	GETNIB
	RET	C
	ADD	A,A
	ADD	A,A
	ADD	A,A
	ADD	A,A
	PUSH	AF
	CALL	GETNIB
	POP	BC
	RET	C
	ADD	A,B		; NC
	RET
;...e
;...sGETNIB:0:
;
; Get nibble
;   CY=0, A=nibble
;   CY=1, otherwise
;
GETNIB:	CALL	GETKEY
	CP	'0'
	RET	C
	CP	'9'+1
	JR	NC,GETNI2
; 0-9
	PUSH	AF
	CALL	PRTCHR
	POP	AF
;
	SUB	'0'		; NC
	RET
;
GETNI2:	CP	'a'
	RET	C
	CP	'f'+1
	JR	NC,GETNI4
; a-f
	PUSH	AF
	CALL	PRTCHR
	POP	AF
;
	SUB	'a'-10		; NC
	RET
;
GETNI4:	SCF
	RET
;...e
;...sGETKEY:0:
;
; Get a keypress, ignoring key-releases
;
GETKEY:	CALL	KB_GET
	CP	KD_RELE
	JR	NZ,GETKE2
	CALL	KB_NEXT
	CALL	KB_GET
	CALL	KB_NEXT
	JR	GETKEY
;
GETKE2:	PUSH	AF
	CALL	KB_NEXT
	POP	AF
	CP	128
	JR	NC,GETKEY
;
	RET
;...e

;...sPRTSTR:0:
;
; Print nul terminated string at HL
;
PRTSTR:	LD	A,(HL)
	AND	A
	RET	Z
	PUSH	HL
	CALL	PRTCHR
	POP	HL
	INC	HL
	JR	PRTSTR
;...e
;...sPRTWOR:0:
;
; Print word HL
;
PRTWOR:	PUSH	HL
	LD	A,H
	CALL	PRTBYT
	POP	HL
	LD	A,L
; fall
;...e
;...sPRTBYT:0:
;
; Print hex value A
;
PRTBYT:	PUSH	AF
	SRL	A
	SRL	A
	SRL	A
	SRL	A
	CALL	PRTNIB
	POP	AF
	AND	00FH
; fall
;...e
;...sPRTNIB:0:
;
; Print nibble A
;
PRTNIB:	ADD	A,'0'
	CP	'9'+1
	JR	C,PRTNI2
	ADD	A,'a'-10-'0'
PRTNI2:
; fall
;...e
;...sPRTCHR:0:
;
; Print character at A
;
PRTCHR:	CP	CR
	JR	NZ,PRTCH2
; newline
	CALL	SCROLL
	EX	AF,AF'
	LD	A,1
	EX	AF,AF'
	RET
;
PRTCH2:	CP	LF
	JR	NZ,PRTCH4
; newline
	EX	AF,AF'
	LD	A,1
	EX	AF,AF'
	RET
; regular character
PRTCH4:	EX	AF,AF'
	LD	E,A
	EX	AF,AF'
	PUSH	AF
	LD	D,0
	LD	HL,PATNAM+23*32
	ADD	HL,DE
	EX	DE,HL
	CALL	VSETOT
	POP	AF
	LD	C,A
	CALL	VDOUTP
	EX	AF,AF'
	INC	A
	CP	32
	JR	NZ,PRTCH6
	CALL	SCROLL
	XOR	A
PRTCH6:	EX	AF,AF'
	RET
;...e
;...sSCROLL:0:
;
; Scroll the screen
;
SCROLL:	PUSH	AF
	LD	DE,PATNAM
	LD	B,23
;
SCROL2:	PUSH	BC
	PUSH	DE
	LD	HL,32
	ADD	HL,DE
	EX	DE,HL
	CALL	VSETRD
	LD	B,32
	LD	HL,SCRBUF
SCROL4:	CALL	VDINPT
	LD	(HL),C
	INC	HL
	DJNZ	SCROL4
	POP	DE
	CALL	VSETOT
	LD	B,32
	LD	HL,SCRBUF
SCROL6:	LD	C,(HL)
	INC	HL
	CALL	VDOUTP
	DJNZ	SCROL6
	LD	HL,32
	ADD	HL,DE
	EX	DE,HL
	POP	BC
	DJNZ	SCROL2
;
	LD	B,32
	LD	C,' '
SCROL8:	CALL	VDOUTP
	DJNZ	SCROL8
;
	POP	AF
	RET
;...e

;...sKB_HW:0:
;
; Is there a keyboard attached
;
KB_HW:	IN	A,(P_KBD)
	LD	B,A
	IN	A,(P_KBD+1)
	CP	B
	RET	NZ
	IN	A,(P_KBD+2)
	CP	B
	RET	NZ
	IN	A,(P_KBD+3)
	CP	B
	RET	NZ
; all 4 ports return the same value, which is good
; if nothing was there, each IN returns the last thing put on the bus
; this would be the last byte of the opcode, P_KBD+x, different each time
	INC	A
	JR	NZ,KB_HW2
	DEC	A		; NZ => not present
	RET
; didn't get a HI-Z response
; if nothing was there, the bus could float high
KB_HW2:	XOR	A		; Z => present
	RET
;...e
;...sKB_INIT:0:
;
; Initialise the keyboard
;
KB_INIT:
	LD	A,KC_INIT
	OUT	(P_KBD),A	; send init
KB_INI2:
	CALL	KB_INP		; wait until its done
	CP	KD_INIT
	JR	NZ,KB_INI2
	RET
;...e
;...sKB_PEEK:0:
;
; Peek the next available keycode
; Returns KD_NONE if there isn't one yet
;
KB_PEEK:
	LD	A,KC_PEEK
	OUT	(P_KBD),A	; send peek
KB_PEE2:
	CALL	KB_INP		; wait until its done
	CP	KD_PEEK
	JR	NC,KB_PEE2
	RET
;...e
;...sKB_GET:0:
;
; Get the next available keycode
; Wait until it appears
;
KB_GET:
	LD	A,KC_PEEK
	OUT	(P_KBD),A	; send peek
KB_GET2:
	CALL	KB_INP		; wait until its done
	CP	KD_PEEK
	JR	NC,KB_GET2
	CP	KD_NONE		; no keycode as yet
	JR	Z,KB_GET2
	RET
;...e
;...sKB_NEXT:0:
;
; Consume the keycode
;
KB_NEXT:
	LD	A,KC_NEXT
	OUT	(P_KBD),A	; send next
KB_NEX2:
	CALL	KB_INP		; wait until its down
	CP	KD_NEXT
	JR	NZ,KB_NEX2
	RET
;...e
;...sKB_INP:0:
;
; Input from the keyboard port
; The ARM chip is running on a different clock to the Z80
; so the value on the port could change whilst we look at it
; (which means we could see a mix of old and new bits in the byte)
; so keep reading it until we get the same value twice
;
KB_INP:	IN	A,(P_KBD)
KB_INP2:
	LD	C,A
	IN	A,(P_KBD)
	CP	C
	JR	NZ,KB_INP2
	RET
;...e

.COMMENT %
;...sdummy keyboard routines:0:
KB_HW:	XOR	A
	RET
KB_INIT:
	EXX
	LD	HL,KBUF
	EXX
	RET
KB_GET:	EXX
	LD	A,(HL)
	EXX
	RET
KB_NEXT:
	EXX
	LD	A,(HL)
	AND	A
KB_NE2:	JR	Z,KB_NE2
	INC	HL
	EXX
	RET
;
KBUF:	DB	'i30'
	DB	'o3055'
	DB	'r6000',CR,ESC
	DB	'w6000112233445566778899',ESC
	DB	'r6000',ESC
	DB	'r6100',ESC
	DB	'r6200',ESC
	DB	'k800c'
	DB	0
;...e
%

;...sVCOPY:0:
;
; VCOPY - copy from HL, to DE in VRAM, BC bytes
;
VCOPY:	CALL	VSETOT
	LD	D,B
	LD	E,C
VCOPY2:	LD	C,(HL)
	INC	HL
	CALL	VDOUTP
	DEC	DE
	LD	A,D
	OR	E
	JR	NZ,VCOPY2
	RET
;...e
;...sVSET:0:
;
; VSET - copy byte A, to DE in VRAM, BC bytes
;
VSET:	CALL	VSETOT
	LD	D,B
	LD	E,C
	LD	C,A
VSET2:	CALL	VDOUTP
	DEC	DE
	LD	A,D
	OR	E
	JR	NZ,VSET2
	RET
;...e

;...svectors:0:
;
; Dummy vectors for now
;
RST08V:
RST10V:
RST18V:
RST18V:
RST20V:
RST28V:
RST30V:
RST38V:	
	RET
NMIV:	IN	A,(P_VDPCI)
	RETN
;...e
;
; CHARSET - Character set
;
CHARSET:
	INCLUDE	CHARSET.INC
;
	INCLUDE VIDEO.INC
;
	INCLUDE SOUND.INC
;
.DEPHASE

; PAD to 8KB
	ORG	02000H+100H

;
END

